#include <string.h>

#include <linux/sched.h>
#include <linux/kernel.h>

#define clear_block(addr) \
    __asm__("cld\n\t" \
            "rep\n\t" \
            "stosl" \
            ::"a" (0),"c" (BLOCK_SIZE/4),"D" ((long) (addr)):)


#define set_bit(nr,addr) ({\
    register int res __asm__("ax"); \
    __asm__ __volatile__("btsl %2,%3\n\tsetb %%al": \
            "=a" (res):"a" (0),"r" (nr),"m" (*(addr))); \
    res;})

#define clear_bit(nr, addr) ({\
    register int res __asm__("ax"); \
    __asm__ __volatile__("btrl %2,%3\n\tsetnb %%al": \
            "=a" (res):"a" (0),"r" (nr),"m" (*(addr))); \
    res;})


#define find_first_zero(addr) ({ \
    int __res; \
    __asm__("cld\n" \
        "1:\tlodsl\n\t" \
        "notl %%eax\n\t" \
        "bsfl %%eax,%%edx\n\t" \
        "je 2f\n\t" \
        "addl %%edx,%%ecx\n\t" \
        "jmp 3f\n" \
        "2:\taddl $32,%%ecx\n\t" \
        "cmpl $8192,%%ecx\n\t" \
        "jl 1b\n" \
        "3:" \
        :"=c" (__res):"c" (0),"S" (addr):"ax","dx"); \
    __res;})

int free_block(int dev, int block) {
    struct super_block * sb;
    struct buffer_head * bh;

    if (!(sb = get_super(dev))) {
        printk("trying to free block on nonexistent device");
    }

    if (block < sb->s_firstdatazone || block >= sb->s_nzones)
        printk("trying to free block not in datazone");

    bh = get_hash_table(dev,block);
    if (bh) {
        if (bh->b_count > 1) {
            brelse(bh);
            return 0;
        }
        bh->b_dirt=0;
        bh->b_uptodate=0;
        if (bh->b_count)
            brelse(bh);
    }

    block -= sb->s_firstdatazone - 1;
    if (clear_bit(block&8191,sb->s_zmap[block/8192]->b_data)) {
        printk("block (%04x:%d) ",dev,block+sb->s_firstdatazone-1);
        printk("free_block: bit already cleared\n");
    }
    sb->s_zmap[block/8192]->b_dirt = 1;
    return 1;
} 

int new_block(int dev) {
    struct buffer_head * bh;
    struct super_block * sb;
    int i, j;

    if (!(sb = get_super(dev))) {
        printk("trying to get new block from nonexistant device");
    }

    j = 8192;
    for (i=0 ; i<8 ; i++) {
        if ((bh = sb->s_zmap[i])) {
            if ((j = find_first_zero(bh->b_data))<8192)
                break;
        }
    }
    if (i>=8 || !bh || j >= 8192) {
        return 0;
    }

    if (set_bit(j, bh->b_data))
        printk("new_block: bit already set");

    bh->b_dirt = 1;
    j += i*8192 + sb->s_firstdatazone-1;
    if (j >= sb->s_nzones)
        return 0;

    if (!(bh=getblk(dev,j)))
        printk("new_block: cannot get block");
    if (bh->b_count != 1)
        printk("new block: count is != 1");
    clear_block(bh->b_data);
    bh->b_uptodate = 1;
    bh->b_dirt = 1;
    brelse(bh);
    return j;
}

struct m_inode * new_inode(int dev) {
    struct m_inode * inode;
    struct super_block * sb;
    struct buffer_head * bh;
    int i, j;

    if (!(inode=get_empty_inode()))
        return NULL;

    if (!(sb = get_super(dev)))
        printk("new_inode with unknown device");

    j = 8192;
    for (i=0 ; i<8 ; i++) {
        if ((bh=sb->s_imap[i])) {
            if ((j=find_first_zero(bh->b_data))<8192)
                break;
        }
    }

    if (!bh || j >= 8192 || j+i*8192 > sb->s_ninodes) {
        iput(inode);
        return NULL;
    }

    if (set_bit(j,bh->b_data))
        printk("new_inode: bit already set");

    bh->b_dirt = 1;
    inode->i_count = 1;
    inode->i_nlinks = 1;
    inode->i_dev = dev;
    inode->i_uid = current->euid;
    inode->i_gid = current->egid;
    inode->i_dirt=1;
    inode->i_num = j + i*8192;
    inode->i_mtime = inode->i_atime = 0;
    return inode;
}

